package com.library.management.entity;

import com.library.management.enums.BorrowStatus;
import com.library.management.enums.UserRole;
import com.library.management.enums.UserStatus;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * 用户实体类
 * 核心业务实体，管理系统所有用户信息
 *
 * 关联关系：
 * - 一对多：借阅记录、预约记录、通知消息、罚款记录
 *
 * 业务功能：
 * - 用户认证和权限管理
 * - 借阅资格验证
 * - 用户状态管理
 *
 * @author 开发者
 * @version 1.0
 * @since 2025-10-19
 */
@Entity
@Table(name = "users", uniqueConstraints = {
        @UniqueConstraint(columnNames = "username", name = "uk_users_username"),
        @UniqueConstraint(columnNames = "email", name = "uk_users_email")
})
@Data
@EqualsAndHashCode(exclude = {"borrowRecords", "reservations", "notifications", "fines"})
@ToString(exclude = {"borrowRecords", "reservations", "notifications", "fines"})
public class User {

    // ========== 主键标识 ==========

    /**
     * 用户ID - 主键，自增长
     * 数据库字段：id (BIGINT, PRIMARY KEY, AUTO_INCREMENT)
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // ========== 基本信息 ==========

    /**
     * 用户名 - 唯一登录标识
     * 数据库字段：username (VARCHAR(50), UNIQUE, NOT NULL)
     */
    @Column(unique = true, nullable = false, length = 50)
    private String username;

    /**
     * 加密密码 - 使用BCrypt加密存储
     * 数据库字段：password (VARCHAR(100), NOT NULL)
     */
    @Column(nullable = false, length = 100)
    private String password;

    /**
     * 邮箱 - 唯一，用于找回密码和通知
     * 数据库字段：email (VARCHAR(100), UNIQUE, NOT NULL)
     */
    @Column(unique = true, nullable = false, length = 100)
    private String email;

    /**
     * 手机号 - 用于联系和验证
     * 数据库字段：phone (VARCHAR(20))
     */
    @Column(length = 20)
    private String phone;

    /**
     * 真实姓名 - 用户真实姓名
     * 数据库字段：real_name (VARCHAR(50), NOT NULL)
     */
    @Column(name = "real_name", nullable = false, length = 50)
    private String realName;

    /**
     * 身份证号 - 身份验证
     * 数据库字段：id_card (VARCHAR(18))
     */
    @Column(name = "id_card", length = 18)
    private String idCard;

    // ========== 业务属性 ==========

    /**
     * 用户角色 - 控制系统权限
     * ADMIN: 系统管理员，LIBRARIAN: 图书管理员，READER: 普通读者
     * 数据库字段：role (ENUM), NOT NULL
     */
    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    private UserRole role = UserRole.READER;

    /**
     * 用户状态 - 控制账户可用性
     * ACTIVE: 活跃，INACTIVE: 未激活，SUSPENDED: 已停用
     * 数据库字段：status (ENUM), NOT NULL
     */
    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    private UserStatus status = UserStatus.ACTIVE;

    /**
     * 最大借书数量 - 限制用户同时借阅的图书数量
     * 数据库字段：max_borrow_count (INT), DEFAULT 5
     */
    @Column(name = "max_borrow_count")
    private Integer maxBorrowCount = 5;

    /**
     * 头像URL - 用户头像图片地址
     * 数据库字段：avatar_url (VARCHAR(200))
     */
    @Column(name = "avatar_url", length = 200)
    private String avatarUrl;

    // ========== 关联关系 ==========

    /**
     * 借阅记录列表 - 用户的所有借阅历史
     * 一对多关系，级联操作：所有操作
     * 延迟加载提高性能
     */
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<BorrowRecord> borrowRecords = new ArrayList<>();

    /**
     * 预约记录列表 - 用户的所有图书预约
     * 一对多关系，级联操作：所有操作
     */
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Reservation> reservations = new ArrayList<>();

    /**
     * 通知消息列表 - 用户接收的所有通知
     * 一对多关系，级联操作：所有操作
     */
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Notification> notifications = new ArrayList<>();

    /**
     * 罚款记录列表 - 用户的所有罚款记录
     * 一对多关系，级联操作：所有操作
     */
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Fine> fines = new ArrayList<>();

    // ========== 审计字段 ==========

    /**
     * 创建人ID - 记录创建者
     * 数据库字段：created_by (BIGINT)
     */
    @Column(name = "created_by")
    private Long createdBy;

    /**
     * 更新人ID - 记录最后修改者
     * 数据库字段：updated_by (BIGINT)
     */
    @Column(name = "updated_by")
    private Long updatedBy;

    /**
     * 创建时间 - 记录创建时间，自动生成
     * 数据库字段：created_time (DATETIME), DEFAULT CURRENT_TIMESTAMP
     */
    @CreationTimestamp
    @Column(name = "created_time", updatable = false)
    private LocalDateTime createdTime;

    /**
     * 更新时间 - 记录最后修改时间，自动更新
     * 数据库字段：updated_time (DATETIME), DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
     */
    @UpdateTimestamp
    @Column(name = "updated_time")
    private LocalDateTime updatedTime;

    // ========== 业务方法 ==========

    /**
     * 检查用户是否可以借书
     * 验证条件：用户状态为活跃，且当前借阅数量小于最大借阅数量
     *
     * @return true-可以借书，false-不可借书
     */
    public boolean canBorrowBook() {
        return this.status == UserStatus.ACTIVE &&
                getCurrentBorrowCount() < this.maxBorrowCount;
    }

    /**
     * 获取当前借阅数量
     * 统计状态为借阅中、逾期、已续借的借阅记录数量
     *
     * @return 当前借阅的图书数量
     */
    public long getCurrentBorrowCount() {
        if (this.borrowRecords == null) {
            return 0;
        }
        return this.borrowRecords.stream()
                .filter(record -> record.getStatus() == BorrowStatus.BORROWED ||
                        record.getStatus() == BorrowStatus.OVERDUE ||
                        record.getStatus() == BorrowStatus.RENEWED)
                .count();
    }

    /**
     * 检查用户是否为管理员
     *
     * @return true-是管理员，false-不是管理员
     */
    public boolean isAdmin() {
        return this.role == UserRole.ADMIN;
    }

    /**
     * 检查用户是否为图书管理员
     *
     * @return true-是图书管理员，false-不是图书管理员
     */
    public boolean isLibrarian() {
        return this.role == UserRole.LIBRARIAN;
    }

    /**
     * 检查用户是否为普通读者
     *
     * @return true-是普通读者，false-不是普通读者
     */
    public boolean isReader() {
        return this.role == UserRole.READER;
    }

    /**
     * 获取用户显示名称
     * 优先显示真实姓名，如果没有则显示用户名
     *
     * @return 用户显示名称
     */
    public String getDisplayName() {
        return this.realName != null ? this.realName : this.username;
    }
}